package gsw.tool.pluggable.core;

import android.content.ComponentName;
import android.content.pm.ApplicationInfo;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.WeakHashMap;

import dalvik.system.DexClassLoader;
import gsw.tool.pluggable.utils.reflect.PluginDirHelper;

/**
 * 插件工具类。
 * 用来Hook系统的ClassLoader对象，由于不能直接修改ClassLoader对象，所以修改ClassLoader的上级对象loadedAPK，
 * 将插件APK的loadedPAK对象插入宿主的ActivityThread的mPackages中，然后从插件的loadedAPK中获取calssLoader，从而实现替换系统ClassLoader对象。
 * 因为插件APK没有安装到系统中，是由自定义的PMS管理的，所以需要通把插件APK的loadedAPK对象插入到宿主中，即把插件的类插入到宿主中。
 */
public class PluginTool {
    //保存插件的包名和loadedAPK对象
    private static Map<String, Object> sPluginLoadedApkCache = new WeakHashMap<String, Object>();

    /**
     * 按需加载插件APK的loadedApk对象到宿主中
     *
     * @param component 组件的ComponentName
     */
    public static void preLoadApk(ComponentName component) {
        Object loadedAPK = sPluginLoadedApkCache.get(component.getPackageName());
        //如果插件APK的loadedAPK对象已经加载到宿主中，就直接退出
        if (loadedAPK != null) {
            return;
        }
        try {
            /**
             * 获取系统的activityThread对象
             */
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            Object currentActivityThread = currentActivityThreadMethod.invoke(null);

            /**
             * 获取ActivityThread的mPackages对象
             * ActivityThread的mPackages对象是用来保存loadedApk对象，加载插件类就是把插件的loadedApk对象插到mPackages中
             */
            Field mPackagesField = activityThreadClass.getDeclaredField("mPackages");
            mPackagesField.setAccessible(true);
            Map mPackages = (Map) mPackagesField.get(currentActivityThread);

            /**
             * 生成插件的loadedApk对象
             * 使用ActivityThread的getPackageInfoNoCheck方法生成loadedApk对象，需要两个参数ApplicationInfo和CompatibilityInfo
             */
            Class<?> compatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo");
            //得到getPackageInfoNoCheck方法
            Method getPackageInfoNoCheckMethod = activityThreadClass.getDeclaredMethod(
                    "getPackageInfoNoCheck", ApplicationInfo.class, compatibilityInfoClass);
            //生成默认的CompatibilityInfo对象
            Field defaultCompatibilityInfoField = compatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO");
            Object defaultCompatibilityInfo = defaultCompatibilityInfoField.get(null);
            //生成插件的ApplicationInfo对象
            ApplicationInfo applicationInfo = PluginManager.getInstance().getApplicationInfo(component, 0);
            //生成插件的loadedApk对象
            loadedAPK = getPackageInfoNoCheckMethod.invoke(currentActivityThread, applicationInfo, defaultCompatibilityInfo);

            /**
             * 设置插件的loadedApk的mClassLoader对象
             */
            String optimizedDirectory = PluginDirHelper.getPluginDalvikCacheDir(PluginManager.getContext(), component.getPackageName());
            String libraryPath = PluginDirHelper.getPluginNativeLibraryDir(PluginManager.getContext(), component.getPackageName());
            ClassLoader classLoader = new MyClassLoader(applicationInfo.publicSourceDir, optimizedDirectory, libraryPath, PluginManager.getContext().getClassLoader());
            Field mClassLoaderField = loadedAPK.getClass().getDeclaredField("mClassLoader");
            mClassLoaderField.setAccessible(true);
            mClassLoaderField.set(loadedAPK, classLoader);

            /**
             * 把插件的loadedApk对象插入ActivityThread的mPackages中
             */
            WeakReference weakReference = new WeakReference(loadedAPK);
            mPackages.put(component.getPackageName(), weakReference);
            sPluginLoadedApkCache.put(component.getPackageName(), loadedAPK);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 自定义类加载器
     */
    public static class MyClassLoader extends DexClassLoader {
        public MyClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {
            super(dexPath, optimizedDirectory, libraryPath, parent);
        }
    }
}
